package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.media.model.dto.MediaDTO;
import com.xuecheng.api.media.model.dto.TeachplanMediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.agent.MediaApiAgent;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ex.TeachplanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private TeachplanMediaService teachplanMediaService;

    @Autowired
    private MediaApiAgent mediaApiAgent;


    /*
    * 根据id查询课程计划
    * */
    @Override
    public TeachplanDTO queryTeachplan(Long courseId, Long companyId) {


       // 业务分析：
       //   1.判断关键业务数据
       //       courseBaseId
        if(ObjectUtils.isEmpty(companyId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

       //   2.查询课程基本信息是否存在（课程计划是没有独立存在的意义）
        CourseBase courseBase = courseBaseService.getById(courseId);

        if(ObjectUtils.isEmpty(courseBase)){
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        //   3.判断课程信息是否被删除
        if(CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())){
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }

       //   4.判断课程是否是同一个教学机构
        if(!ObjectUtils.nullSafeEquals(courseBase.getCompanyId(),companyId)){
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }

       //   5.根据课程id查询课程计划树形结构
        TeachplanMapper baseMapper = this.getBaseMapper();

        List<TeachplanNode> teachplanList = baseMapper.queryTeachPlan(courseId);

        //   6.判断是否有课程计划
        if(CollectionUtils.isEmpty(teachplanList)){
          return new TeachplanDTO();
        }

       //   7.如果有课程计划，递归生成树形结构
        TeachplanNode nodes = teachplanList.remove(0);

        //调用递归方法
        generateTreeNodes(teachplanList,nodes);

        //   8.转换为dto数据并返回
        TeachplanDTO teachplanDTO = TeachplanConvert.INSTANCE.teach2dto(nodes);

        return teachplanDTO;
    }


    /**
     * 递归生成数据结构
     */
    private void generateTreeNodes(List<TeachplanNode> teachplanList, TeachplanNode nodes) {

        //判断父级集合是否为空
        if(ObjectUtils.isEmpty(nodes.getChildrenNodes())){
            ArrayList<TeachplanNode> teachplanNodes = new ArrayList<>();
            nodes.setChildrenNodes(teachplanNodes);
        }

        //遍历
        for (TeachplanNode teachplanNode : teachplanList) {

            //判断teachplanNode是不是父级和的字节点
            if(ObjectUtils.nullSafeEquals(teachplanNode.getParentid(),nodes.getId())){

                //将子节点数据赋值给父节点中的集合
                nodes.getChildrenNodes().add(teachplanNode);

                //自己调用自己，父节点的等级不能是3等级
                if(!TeachPlanEnum.THIRD_LEVEL.equals(teachplanNode.getGrade())){
                    generateTreeNodes(teachplanList,teachplanNode);
                }else {
                    //如果是三级，查询是否关联媒资信息
                    LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();

                    queryWrapper.eq(TeachplanMedia::getTeachplanId, teachplanNode.getId());

                    TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);

                    teachplanNode.setTeachplanMedia(teachplanMedia);

                }
            }
        }
    }


    /*
    * 创建或修改课程计划
    * 根据Teachplan是否有id
    *   有：修改
    *   无：创建
    * */
    @Override
    @Transactional
    public TeachplanDTO createdOrModifyTeachplan(TeachplanDTO teachplanDTO, Long companyId) {

             TeachplanDTO resultDTO = null;

             if(ObjectUtils.isEmpty(teachplanDTO.getTeachPlanId())){
                 //无：创建课程计划
                 resultDTO = createdTeachplan(teachplanDTO,companyId);
             }else {
                 //有：修改课程计划
                 resultDTO = modifyTeachplan(teachplanDTO,companyId);
             }

        return resultDTO;
    }


    /*
   * 新增操作--课程计划
   *     1.判断关键数据
   *         pname:课程计划名称
   *         companyId：机构id值
   *     2.判断是否有课程数据：courseId
   *     3.判断课程数据是否存在
   *     4.判断课程审核状态
   *     5.判断课程是否删除
   *     6.判断是否是同一个机构
   *     7.获得父级节点数据
   *     8.将当前的课程计划和父级数据进行关联
   *     9.保存课程基本信息数据
   *       判断保存的结果
   *     10.将po数据转为dto并返回
   * */
    //创建课程计划
    private TeachplanDTO createdTeachplan(TeachplanDTO teachplanDTO, Long companyId) {

        //1.判断关键数据
        //  pname:课程计划名称
        //  companyId：机构id值
        if(ObjectUtils.isEmpty(companyId) || StringUtil.isBlank(teachplanDTO.getPname())){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //2.判断是否有课程数据：courseId
        CourseBase courseBase = courseBaseService.getById(teachplanDTO.getCourseId());

        //3.判断课程数据是否存在
        if(ObjectUtils.isEmpty(courseBase)){
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        //4.判断课程审核状态
        if(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBase.getAuditStatus())){
            //throw new RuntimeException("课程已提交或审核已通过");
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        //5.判断课程是否删除
        if(CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())){
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }

        //6.判断是否是同一个机构
        if(!ObjectUtils.nullSafeEquals(companyId,courseBase.getCompanyId())){
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }

        //7.获得父级节点数据
        Teachplan parenNode =  getParentNode(teachplanDTO,courseBase);

        //将dto转为po
        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2teachplan(teachplanDTO);

        //8.将当前的课程计划和父级数据进行关联
        teachplan.setParentid(parenNode.getId());

        // 设置当前课程计划的级别。 获得是
        teachplan.setGrade(parenNode.getGrade()+1);

        // 设置当前课程计划的排序orderBy
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(Teachplan::getParentid,parenNode.getId());

        int count = this.count(queryWrapper);

        //  当前的teachplan的orderby：父级拥有的子级数据的个数+1
        teachplan.setOrderby(count+1);

        //9.保存课程基本信息数据
        boolean result = this.save(teachplan);

        //判断保存的结果
        if(!result){
                ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        //10.将po数据转为dto并返回
        TeachplanDTO teachplanDTO1 = TeachplanConvert.INSTANCE.po2dto(teachplan);

        return teachplanDTO1;
    }

    /*
    * 获得课程计划的父级节点
    *
    *   1.课程计划的一级（根级）
    *
    *    - 数据来源：课程基础信息（CourseBase）
    *    - 创建时机：在第一次添加二级数据时自动生成
    *    - 注意事项：一级父级（根级）父级 ID 值为 0
    *
    *    2.课程计划的二级和三级节点
    *
    *    - 数据来源：教育机构人员在页面填写的数据
    *    - 创建时机：教育机构人员提交课程计划数据
    *    - 注意事项：添加时要校验父级菜单是否存在
    *      - 二级在添加时
    *        - 父级不存在（一级）：自动创建并获得获得父级Id值
    *        - 父级存在（一级）：获得父级Id值
    *      - 三级在添加时
    *        - 父级不存在（二级）：抛出异常
    *        - 父级存在（二级）：获得父级Id值
    *
    *   机构添加的课程计划是能是：二级或三级
    *   判断新增的数据是二级或三级：依据是当前Teachplan 是否有parentId值
    *
    * */
    private Teachplan getParentNode(TeachplanDTO teachplanDTO, CourseBase courseBase) {


        Long parenId = teachplanDTO.getParentid();
        // 1.判断新增的数据是二级或三级：依据是当前Teachplan 是否有parentId值
        //TODO  为什么paren为空就是二级
        //前端只能修改二级和三级的数据，所以
        if(ObjectUtils.isEmpty(parenId)) {

            // 新增的课程计划是二级数据，需要获得一级课程计划,根据课程id和父节点id
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId,parenId);
            queryWrapper.eq(Teachplan::getCourseId,courseBase.getId());

            // 获得一级课程计划
            Teachplan teachplan = this.getOne(queryWrapper);

            // - 父级不存在（一级）：自动创建并获得获得父级Id值
            if(ObjectUtils.isEmpty(teachplan)){
                // - 父级不存在（一级）：自动创建并获得获得父级Id值
                teachplan = new Teachplan();
                teachplan.setPname(courseBase.getName());
                teachplan.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                teachplan.setGrade(TeachPlanEnum.FIRST_LEVEL);
                teachplan.setDescription(courseBase.getDescription());
                teachplan.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                teachplan.setCourseId(courseBase.getId());

                boolean result = this.save(teachplan);

                if(!result){
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
        // - 父级存在（一级）直接返回：获得父级Id值
            return teachplan;
        }

        // 新增的课程计划是三级数据，需要获得二级课程计划
        Teachplan teachplan = this.getById(teachplanDTO.getParentid());

        //判断二级是否存在
        if(ObjectUtils.isEmpty(teachplan)){
            ExceptionCast.cast(ContentErrorCode.E_120408);
        }

        // 获得二级课程计划数据
        return teachplan;
    }



    //修改课程计划
    private TeachplanDTO modifyTeachplan(TeachplanDTO teachplanDTO, Long companyId) {

       // 1.判断关键数据 课程计划名称和机构id不能为空
        if(StringUtil.isBlank(teachplanDTO.getPname()) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //2.判断是否有课程数据：courseId
        CourseBase courseBase = courseBaseService.getById(teachplanDTO.getCourseId());

        //3.判断课程数据是否存在
        if(ObjectUtils.isEmpty(courseBase)){
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        //4.判断课程审核状态
        if(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBase.getAuditStatus())){
            //throw new RuntimeException("课程已提交或审核已通过");
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        //5.判断课程是否删除
        if(CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())){
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }

        //6.判断是否是同一个机构
        if(!ObjectUtils.nullSafeEquals(companyId,courseBase.getCompanyId())){
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }

        //判断课程计划是否存在
        Teachplan teachplan = this.getById(teachplanDTO.getTeachPlanId());

        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        //  7.修改课程计划数据
       //     有些数据是不让前端来修改
        //   parentid
        teachplanDTO.setParentid(teachplan.getParentid());
        //   grade
        teachplanDTO.setGrade(String.valueOf(teachplan.getGrade()));
        //   orderby
        teachplanDTO.setOrderby(teachplan.getOrderby());
        //   courseid
        teachplanDTO.setCourseId(teachplan.getCourseId());

        //将dto转为po
        Teachplan dto2teachplan = TeachplanConvert.INSTANCE.dto2teachplan(teachplanDTO);

        boolean update = this.updateById(dto2teachplan);

        //   8.判断修改的结果
        if(!update){
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

       //   9.返回结果数据 po-> dto
        return teachplanDTO;
    }



    /*
     * 业务分析；
     *   1.判断关键数据
     *       teachplanId、mediaId、companyId、mediaFilename
     *
     *   2.判断课程的审核状态和课程信息是否被删除
     *
     *   3.判断课程是否是同一家机构
     *
     *   4.查询媒资信息并判断是否有权限关联
     *       是否是同一家机构
     *
     *   5.判断是否是三级课程计划
     *
     *   6.判断课程计划是否有关联数据
     *       teachplanId来作为关键数据
     *
     *       如果存在：查询课程计划关联媒资数据并修改
     *       如果不存在：创建课程计划数据关联媒资
     *
     *   7.转换为dto并返回数据
     * */

    //课程计划绑定媒资
    @Transactional
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {

        //  1.判断关键数据
        //      teachplanId、mediaId、companyId、mediaFilename
        if(ObjectUtils.isEmpty(dto.getTeachplanId()) || ObjectUtils.isEmpty(dto.getMediaId()) ||
                ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //  2.判断课程的审核状态和课程信息是否被删除
        Teachplan teachplan = this.getById(dto.getTeachplanId());

        if(ObjectUtils.isEmpty(teachplan)){
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());

        if(ObjectUtils.isEmpty(courseBase)){
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }

        if(CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())){
            ExceptionCast.cast(ContentErrorCode.E_120021);
        }

        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus())
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120015);
        }


        //3.判断课程是否是同一家机构
        if(!companyId.equals(courseBase.getCompanyId())){
            ExceptionCast.cast(CommonErrorCode.E_403000);
        }

        //  4.查询媒资信息并判断是否有权限关联
        RestResponse<MediaDTO> response = mediaApiAgent.queryMediaById(dto.getMediaId());

        if(!response.isSuccessful()){
            ExceptionCast.castWithCodeAndDesc(response.getCode(),response.getMsg());
        }

        //      是否是同一家机构
            if(!companyId.equals(response.getResult().getCompanyId())){
                ExceptionCast.cast(CommonErrorCode.E_403000);
            }
        //  5.判断是否是三级课程计划
        if(!teachplan.getGrade().equals(TeachPlanEnum.THIRD_LEVEL)){
            ExceptionCast.cast(ContentErrorCode.E_120410);
        }

        //  6.判断课程计划是否有关联数据
        LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(TeachplanMedia::getTeachplanId,dto.getTeachplanId());

        TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);

        //      如果不存在：创建课程计划数据关联媒资
        if(ObjectUtils.isEmpty(teachplanMedia)){
                teachplanMedia = new TeachplanMedia();

                teachplanMedia.setTeachplanId(teachplan.getId());
                teachplanMedia.setMediaId(response.getResult().getId());
                teachplanMedia.setMediaFilename(response.getResult().getFilename());
                teachplanMedia.setCourseId(courseBase.getId());


                boolean save = teachplanMediaService.save(teachplanMedia);

                if(!save){
                    ExceptionCast.cast(ContentErrorCode.E_120411);
                }

            }else {
            //      如果存在：查询课程计划关联媒资数据并修改
            teachplanMedia.setMediaId(response.getResult().getId());
            teachplanMedia.setMediaFilename(response.getResult().getFilename());

            boolean result = teachplanMediaService.updateById(teachplanMedia);

            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120411);
            }
        }

        //  7.转换为dto并返回数据
        TeachplanMediaDTO teachplanMediaDTO = TeachplanConvert.INSTANCE.entity2dto(teachplanMedia);

        return teachplanMediaDTO;
    }
}
